/**
 * @file ct_uart.c
 * @brief Software implementation of uart for 51 microcontroller.
 * @author David Xu
 * @version 1.0.0
 * @date 2023-12-12
 * @note finished
 */
#include "ct_config.h"
#include "ct_uart.h"

uint8_t UART0_RxBuff[UART_BUFF_SIZE];
uint8_t UART0_TxBuff[UART_BUFF_SIZE];

/* UART0 configuration structure */
UART_t g_Uart0 = {
    CT_UART0,          // UART identifier for UART0
    UART_BUFF_SIZE,    // Size of the UART receive buffer
    0,                 //  Number of counter
    // Pointer to the receive buffer for UART0
    UART0_RxBuff,    // Pointer to the receive buffer for UART0
    0,               // Current position in the receive buffer (rx_index)
    0,               // Expected length of data to receive (rx_length)
    0,               // Timeout counter (rx_timeout)
    // Pointer to the transmit buffer for UART0
    UART0_TxBuff,    // Pointer to the transmit buffer for UART0
    0,               // Current position in the transmit buffer (tx_index)
    0,               // Expected length of data to transmit
    ct_false,        // Flag indicating if a receive operation is complete (rx_bLock)
    ct_false,        // Flag indicating if a transmit operation is in progress (tx_bBusy)
    ct_false,        // Receive timeout flag (rx_bTimeout)
    ct_false,        // Receive overflow flag (rx_bOverflow)
    // NULL          // Pointer to the callback function for UART0
};

/* UART0 interrupt vector demo */
#if defined   __C51__
void          Serial_ISR(void) interrupt 4
#elif defined __ICC8051__
#pragma vector = 0x23
__interrupt void SerialPort0_ISR(void)
#elif defined __SDCC__
void Serial_ISR(void) __interrupt(4)
#endif
{
    SFRS_TMP = SFRS; /* for SFRS page */
    if (RI) {
        RI = 0;    // Clear receive interrupt flag
        ct_uart_IntReceiveBuff(&g_Uart0);
    }
    if (TI) {
        TI = 0;    // Clear transmit interrupt flag
        if (g_Uart0.tx_index < g_Uart0.tx_length) {
            SBUF = g_Uart0.tx_buffer[g_Uart0.tx_index++];
        } else {
            ct_uart_ResetIntSendFlag(&g_Uart0);
        }
    }
    if (SFRS_TMP) /* for SFRS page */
    {
        ENABLE_SFR_PAGE1;
    }
}

#ifdef CT_USE_UART1
/* UART1 interrupt vector demo */
#if defined   __C51__
void          SerialPort1_ISR(void) interrupt 15
#elif defined __ICC8051__
#pragma vector = 0x7B
__interrupt void SerialPort1_ISR(void)
#elif defined __SDCC__
void SerialPort1_ISR(void) __interrupt(15)
#endif
{
    SFRS_TMP = SFRS; /* for SFRS page */
    if (RI_1) {
        RI_1 = 0; /* clear reception flag for next reception */
        // ct_uart_IntRead(&g_Uart1);
    }
    if (TI_1) {
        TI_1 = 0; /* clear transmission flag for next transmission */
        // g_Uart0.tx_bBusy = 0;    // Clear transmit busy status
    }

    if (SFRS_TMP) /* for SFRS page */
    {
        ENABLE_SFR_PAGE1;
    }
}

uint8_t UART1_RxBuff[UART_BUFF_SIZE];
uint8_t UART1_TxBuff[UART_BUFF_SIZE];
/* UART1 configuration structure */
UART_t g_Uart1 = {
    CT_UART1,          // UART identifier for UART1
    UART_BUFF_SIZE,    // Size of the UART receive buffer
    0,                 //  Number of counter
    // Pointer to the receive buffer for UART1
    UART1_RxBuff,    // Pointer to the receive buffer for UART1
    0,               // Current position in the receive buffer (rx_index)
    0,               // Expected length of data to receive (rx_length)
    0,               // Timeout counter (rx_timeout)
    // Pointer to the transmit buffer for UART1
    UART1_TxBuff,    // Pointer to the transmit buffer for UART1
    0,               // Current position in the transmit buffer (tx_index)
    0,               // Expected length of data to transmit
    ct_false,        // Flag indicating if a receive operation is complete (rx_bLock)
    ct_false,        // Flag indicating if a transmit operation is in progress (tx_bBusy)
    ct_false,        // Receive timeout flag (rx_bTimeout)
    // NULL          // Pointer to the callback function for UART1
};
#endif

/**
 * @brief Initialize the UART interface.
 *
 * @param UARTx Pointer to a UART_t structure that contains the configuration information for the UART.
 * @param baudrate The desired baud rate for UART communication.
 */
void ct_uart_Init(UART_t *UARTx, uint32_t baudrate) {
    // Ensure UARTx is not NULL
    if (UARTx == NULL) {
        return;
    }

    if (UARTx->id == CT_UART0) {
        // Configuration for UART0
        P06_QUASI_MODE;
        P07_INPUT_MODE;
        ct_uart_Open(24000000, CT_UART0_TIMER1, baudrate);
        ENABLE_UART0_PRINTF;
        ENABLE_UART0_INTERRUPT; /* Enable UART0 interrupt */
    }
#ifdef CT_USE_UART1
    else if (UARTx->id == CT_UART1) {
        // Configuration for UART1
        P16_QUASI_MODE;
        P02_INPUT_MODE;
        ct_uart_Open(24000000, CT_UART1_TIMER3, baudrate);
        ENABLE_UART1_PRINTF;
        // ENABLE_UART1_INTERRUPT; // Uncomment to enable UART1 interrupt, ISR is in uart.c file
    }
//  ENABLE_GLOBAL_INTERRUPT; /* Global interrupt enable */
#endif
    // UARTx->buf_length = UART_BUFF_SIZE;
    // UARTx->retries    = 3;
    // // RxBuff
    // UARTx->rx_index  = 0;
    // UARTx->rx_length = 0;
    // UARTx->rx_buffer = UART0_RxBuff;
    // // TxBuff
    // UARTx->tx_index  = 0;
    // UARTx->tx_length = 0;
    // UARTx->tx_buffer = UART0_TxBuff;
    // //
    // UARTx->tx_bBusy     = ct_false;
    // UARTx->rx_bLock     = ct_false;
    // UARTx->rx_bTimeout  = ct_false;
    // UARTx->rx_bOverflow = ct_false;
    //  //  UARTx->rx_callback = NULL;
}

/**
 * @brief Open the UART interface with specified baud rate.
 *
 * This function initializes the UART interface with a given system clock and baud rate. It configures the UART port for communication.
 *
 * @param u32SysClock The system clock frequency in Hz.
 * @param Port The UART port to be used.
 * @param u32Baudrate The desired baud rate for UART communication.
 */
void ct_uart_Open(uint32_t       u32SysClock,
                  ct_uart_port_t Port,
                  uint32_t       u32Baudrate) {
    UART_Open(u32SysClock, Port, u32Baudrate);
}

/**
 * @brief Send a byte of data via UART.
 *
 * @param UARTx Pointer to a UART_t structure that contains the configuration information for the UART.
 * @param dat The byte of data to send.
 */
void ct_uart_SendData(UART_t *UARTx, uint8_t dat) {
    if (UARTx == NULL) {
        return;
    }
    if (UARTx->id == CT_UART0) {
        TI   = 0;
        SBUF = dat;
        while (TI == 0)
            ;
        TI = 0;
    }
#ifdef CT_USE_UART1
    else if (UARTx->id == CT_UART1) {
        TI_1   = 0;
        SBUF_1 = dat;
        while (TI_1 == 0)
            ;
        TI_1 = 0;
    }
#endif
}

/**
 * @brief Send a byte of data via UART.
 *
 * @param UARTx Pointer to a UART_t structure that contains the configuration information for the UART.
 * @param dat The byte of data to send.
 */
void ct_uart_SendBuff(UART_t *UARTx, uint8_t dat) {
    if (UARTx == NULL) {
        return;
    }
    UARTx->tx_bBusy = 1;
    // Send data according to UART port
    if (UARTx->id == CT_UART0) {
        TI   = 0;
        SBUF = dat;
    }
#ifdef CT_USE_UART1
    else if (UARTx->id == CT_UART1) {
        TI_1   = 0;
        SBUF_1 = dat;
    }
#endif
}

void ct_uart_ResetIntSendFlag(UART_t *UARTx) {
    if (UARTx == NULL) {
        return;
    }

    UARTx->tx_bBusy = 0;    // Clear sending busy flag
}

/**
 * @brief Send a string of data via UART.
 *
 * This function sends a null-terminated string through the UART interface. It iterates over the string and sends each character until it reaches the null terminator.
 *
 * @param UARTx Pointer to a UART_t structure that contains the configuration information for the UART.
 * @param str Pointer to the string to send.
 */
void ct_uart_SendString(UART_t *UARTx, uint8_t *str) {
    while (*str != '\0') {
        ct_uart_SendData(UARTx, *str++);
    }
}

/**
 * @brief Receive a byte of data from UART.
 *
 * @param UARTx Pointer to a UART_t structure that contains the configuration information for the UART.
 * @return The received byte of data.
 */
uint8_t ct_uart_ReceiveData(UART_t *UARTx) {
    uint8_t dat = 0;
    if (UARTx == NULL) {
        return 0;
    }
    if (UARTx->id == CT_UART0) {
        // Configuration for UART0
        // while (!RI)
        ;
        dat = SBUF;
        RI  = 0;
    }
#ifdef CT_USE_UART1
    else if (UARTx->id == CT_UART1) {
        // Configuration for UART1
        // while (!RI_1)
        ;
        dat  = SBUF_1;
        RI_1 = 0;
    }
#endif
    return dat;
}

/**
 * @brief Receive data from UART in an interrupt context.
 *
 * @param UARTx Pointer to a UART_t structure that contains
 * the configuration information for the UART.
 */
void ct_uart_IntReceiveBuff(UART_t *UARTx) {
    uint8_t dat;

    if (UARTx == NULL || UARTx->rx_bLock) {
        return;
    }

    dat = ct_uart_ReceiveData(UARTx);
    //
    UARTx->rx_timeout = 0;
    if (UARTx->rx_index >= UARTx->buf_length) {
        // Handle overflow
        UARTx->rx_bOverflow = 1;
        UARTx->rx_index     = 0;
        return;
    }

    UARTx->rx_buffer[UARTx->rx_index++] = dat;
}

/**
 * @brief Enable or disable UART interrupt.
 *
 * This function enables or disables the UART interrupt based on the status parameter provided.
 *
 * @param id The identifier for the UART port.
 * @param Status The interrupt status (ENABLE or DISABLE).
 */
void ct_uart_InterruptEnable(ct_uart_port_t id, uint8_t Status) {
    switch (id) {
        case UART0:
            switch (Status) {
                case Disable:
                    DISABLE_UART0_INTERRUPT;
                    break;
                case Enable:
                    ENABLE_UART0_INTERRUPT;
                    break;
            }
            break;
#ifdef CT_USE_UART1
        case UART1:
            switch (Status) {
                case Disable:
                    DISABLE_UART1_INTERRUPT;
                    break;
                case Enable:
                    ENABLE_UART1_INTERRUPT;
                    break;
            }
            break;
#endif
        default:
            break;
    }
}

/**
 * @brief Control the interrupt status of UART ports other than the one specified.
 *
 * This function enables or disables interrupts for UART ports other than the one
 * referenced by the UARTx parameter. It is typically used to prevent interrupt
 * conflicts when multiple UART ports are used.
 *
 * @param UARTx Pointer to a UART_t structure that contains the configuration information for the specified UART.
 * @param enable A flag to specify whether to enable (non-zero value) or disable (zero value) the interrupts.
 */
void ControlUARTInterrupts(UART_t *UARTx, unsigned char enable) {
    if (UARTx == NULL) {
        return;    // If the passed pointer is NULL, return directly
    }

    // Disable or enable interrupts for other UART ports based on the id of the passed UART_t instance
    switch (UARTx->id) {
        case CT_UART0:
            // If it's UART0, disable or enable interrupts for UART1 based on the value of enable
            ct_uart_InterruptEnable(CT_UART1, enable);
            break;
#ifdef CT_USE_UART1
        case CT_UART1:
            // If it's UART1, disable or enable interrupts for UART0 based on the value of enable
            ct_uart_InterruptEnable(CT_UART0, enable);
            break;
            // If there are more UART ports, you can continue to add case statements
#endif
        default:
            break;
    }
}
